// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.

package com.netease.yunxin.kit.entertainment.common.smartrefresh.util;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;

import android.content.res.Resources;
import android.graphics.PointF;
import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.webkit.WebView;
import android.widget.AbsListView;
import android.widget.ListView;
import android.widget.ScrollView;
import androidx.annotation.NonNull;
import androidx.core.view.NestedScrollingChild;
import androidx.core.view.NestedScrollingParent;
import androidx.core.view.ScrollingView;
import androidx.core.widget.NestedScrollView;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.ViewPager;
import com.netease.yunxin.kit.entertainment.common.R;
import com.netease.yunxin.kit.entertainment.common.smartrefresh.api.RefreshComponent;

/** SmartUtil Created by scwang on 2018/3/5. */
public class SmartUtil implements Interpolator {

  public static int INTERPOLATOR_VISCOUS_FLUID = 0;
  public static int INTERPOLATOR_DECELERATE = 1;

  private int type;

  public SmartUtil(int type) {
    this.type = type;
  }

  //<editor-fold desc="内容工具">
  public static int measureViewHeight(View view) {
    ViewGroup.LayoutParams p = view.getLayoutParams();
    if (p == null) {
      p = new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
    }
    int childHeightSpec;
    int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0, p.width);
    if (p.height > 0) {
      childHeightSpec = View.MeasureSpec.makeMeasureSpec(p.height, View.MeasureSpec.EXACTLY);
    } else {
      childHeightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    }
    view.measure(childWidthSpec, childHeightSpec);
    return view.getMeasuredHeight();
  }

  public static void scrollListBy(@NonNull AbsListView listView, int y) {
    if (Build.VERSION.SDK_INT >= 19) {
      // Call the framework version directly
      listView.scrollListBy(y);
    } else if (listView instanceof ListView) {
      // provide backport on earlier versions
      final int firstPosition = listView.getFirstVisiblePosition();
      if (firstPosition == ListView.INVALID_POSITION) {
        return;
      }

      //noinspection UnnecessaryLocalVariable
      final ViewGroup listGroup = listView;
      final View firstView = listGroup.getChildAt(0);
      if (firstView == null) {
        return;
      }

      final int newTop = firstView.getTop() - y;
      ((ListView) listView).setSelectionFromTop(firstPosition, newTop);
    } else {
      listView.smoothScrollBy(y, 0);
    }
  }

  public static boolean isScrollableView(View view) {
    if (view instanceof RefreshComponent) {
      return false;
    }
    return view instanceof AbsListView
        || view instanceof ScrollView
        || view instanceof ScrollingView
        || view instanceof WebView
        || view instanceof NestedScrollingChild;
  }

  public static boolean isContentView(View view) {
    if (view instanceof RefreshComponent) {
      return false;
    }
    return isScrollableView(view)
        || view instanceof ViewPager
        || view instanceof NestedScrollingParent;
  }

  public static void fling(View scrollableView, int velocity) {
    if (scrollableView instanceof ScrollView) {
      ((ScrollView) scrollableView).fling(velocity);
    } else if (scrollableView instanceof AbsListView) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        ((AbsListView) scrollableView).fling(velocity);
      }
    } else if (scrollableView instanceof WebView) {
      ((WebView) scrollableView).flingScroll(0, velocity);
    } else if (scrollableView instanceof NestedScrollView) {
      ((NestedScrollView) scrollableView).fling(velocity);
    } else if (scrollableView instanceof RecyclerView) {
      ((RecyclerView) scrollableView).fling(0, velocity);
    }
  }
  //</editor-fold>

  //<editor-fold desc="滚动判断">

  /**
   * 判断内容是否可以刷新
   *
   * @param targetView 内容视图
   * @param touch 按压事件位置
   * @return 是否可以刷新
   */
  public static boolean canRefresh(@NonNull View targetView, PointF touch) {
    if (targetView.canScrollVertically(-1) && targetView.getVisibility() == View.VISIBLE) {
      return false;
    }
    //touch == null 时 canRefresh 不会动态递归搜索
    if (targetView instanceof ViewGroup && touch != null) {
      ViewGroup viewGroup = (ViewGroup) targetView;
      final int childCount = viewGroup.getChildCount();
      PointF point = new PointF();
      for (int i = childCount; i > 0; i--) {
        View child = viewGroup.getChildAt(i - 1);
        if (isTransformedTouchPointInView(viewGroup, child, touch.x, touch.y, point)) {
          Object tag = child.getTag(R.id.srl_tag);
          if ("fixed".equals(tag) || "fixed-bottom".equals(tag)) {
            return false;
          }
          touch.offset(point.x, point.y);
          boolean can = canRefresh(child, touch);
          touch.offset(-point.x, -point.y);
          return can;
        }
      }
    }
    return true;
  }

  /**
   * 判断内容视图是否可以加载更多
   *
   * @param targetView 内容视图
   * @param touch 按压事件位置
   * @param contentFull 内容是否填满页面 (未填满时，会通过canScrollUp自动判断)
   * @return 是否可以刷新
   */
  public static boolean canLoadMore(@NonNull View targetView, PointF touch, boolean contentFull) {
    if (targetView.canScrollVertically(1) && targetView.getVisibility() == View.VISIBLE) {
      return false;
    }
    //touch == null 时 canLoadMore 不会动态递归搜索
    if (targetView instanceof ViewGroup
        && touch != null
        && !SmartUtil.isScrollableView(targetView)) {
      ViewGroup viewGroup = (ViewGroup) targetView;
      final int childCount = viewGroup.getChildCount();
      PointF point = new PointF();
      for (int i = childCount; i > 0; i--) {
        View child = viewGroup.getChildAt(i - 1);
        if (isTransformedTouchPointInView(viewGroup, child, touch.x, touch.y, point)) {
          Object tag = child.getTag(R.id.srl_tag);
          if ("fixed".equals(tag) || "fixed-top".equals(tag)) {
            return false;
          }
          touch.offset(point.x, point.y);
          boolean can = canLoadMore(child, touch, contentFull);
          touch.offset(-point.x, -point.y);
          return can;
        }
      }
    }
    return (contentFull || targetView.canScrollVertically(-1));
  }
  //</editor-fold>

  //<editor-fold desc="transform Point">

  public static boolean isTransformedTouchPointInView(
      @NonNull View group, @NonNull View child, float x, float y, PointF outLocalPoint) {
    if (child.getVisibility() != View.VISIBLE) {
      return false;
    }
    final float[] point = new float[2];
    point[0] = x;
    point[1] = y;
    //        transformPointToViewLocal(group, child, point);
    point[0] += group.getScrollX() - child.getLeft();
    point[1] += group.getScrollY() - child.getTop();
    //        final boolean isInView = pointInView(child, point[0], point[1], 0);
    final boolean isInView =
        point[0] >= 0
            && point[1] >= 0
            && point[0] < (child.getWidth())
            && point[1] < ((child.getHeight()));
    if (isInView && outLocalPoint != null) {
      outLocalPoint.set(point[0] - x, point[1] - y);
    }
    return isInView;
  }
  //</editor-fold>

  //<editor-fold desc="像素密度">
  private static float density = Resources.getSystem().getDisplayMetrics().density;

  /**
   * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
   *
   * @param dpValue 虚拟像素
   * @return 像素
   */
  public static int dp2px(float dpValue) {
    return (int) (0.5f + dpValue * density);
  }

  /**
   * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
   *
   * @param pxValue 像素
   * @return 虚拟像素
   */
  public static float px2dp(int pxValue) {
    return (pxValue / density);
  }
  //</editor-fold>

  //<editor-fold desc="ViscousFluidInterpolator">
  /** Controls the viscous fluid effect (how much of it). */
  private static final float VISCOUS_FLUID_SCALE = 8.0f;

  private static final float VISCOUS_FLUID_NORMALIZE;
  private static final float VISCOUS_FLUID_OFFSET;

  static {
    // must be set to 1.0 (used in viscousFluid())
    VISCOUS_FLUID_NORMALIZE = 1.0f / viscousFluid(1.0f);
    // account for very small floating-point error
    VISCOUS_FLUID_OFFSET = 1.0f - VISCOUS_FLUID_NORMALIZE * viscousFluid(1.0f);
  }

  private static float viscousFluid(float x) {
    x *= VISCOUS_FLUID_SCALE;
    if (x < 1.0f) {
      x -= (1.0f - (float) Math.exp(-x));
    } else {
      float start = 0.36787944117f; // 1/e == exp(-1)
      x = 1.0f - (float) Math.exp(1.0f - x);
      x = start + x * (1.0f - start);
    }
    return x;
  }

  @Override
  public float getInterpolation(float input) {
    if (type == INTERPOLATOR_DECELERATE) {
      return (1.0f - (1.0f - input) * (1.0f - input));
    }
    final float interpolated = VISCOUS_FLUID_NORMALIZE * viscousFluid(input);
    if (interpolated > 0) {
      return interpolated + VISCOUS_FLUID_OFFSET;
    }
    return interpolated;
  }
  //</editor-fold>

}
